﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace Sys.Core
{
    /// <summary>
    /// Tree基类
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    /// <typeparam name="B">实体操作实例类型</typeparam>
    public class ExampleTree<T, B> : ExampleSort<T, B>
        where T : class, new()
        where B : new()
    {
        /// <summary>
        /// 编号字段名
        /// </summary>
        public virtual string ID { get { return "ID"; } }

        /// <summary>
        /// 路径字段名
        /// </summary>
        public virtual string Path { get { return "Path"; } }

        /// <summary>
        /// 父级字段名
        /// </summary>
        public virtual string Parent { get { return "Parent"; } }

        /// <summary>
        /// 重写新增方法
        /// </summary>
        /// <param name="entity">新增实体</param>
        /// <returns>受影响行数</returns>
        public override int Insert(T entity)
        {
            return base.Insert(UpdatePath(entity));
        }

        /// <summary>
        /// 重写更新方法
        /// </summary>
        /// <param name="entity">更新实体</param>
        /// <returns>受影响行数</returns>
        public override int Update(T entity)
        {
            var model = GetByKey(entity.GetType().GetProperty(ID).GetValue(entity, null));

            string mparent = model.GetType().GetProperty(Parent).GetValue(model, null).ToString();
            string eparent = entity.GetType().GetProperty(Parent).GetValue(entity, null).ToString();

            if (!mparent.Equals(eparent))
            {
                entity = UpdatePath(entity);

                #region 更新子集路径

                string eid = entity.GetType().GetProperty(ID).GetValue(entity, null).ToString();

                var list = Entities.Where(FuncChildren(eid));

                string mpath = model.GetType().GetProperty(Path).GetValue(model, null).ToString();
                string epath = entity.GetType().GetProperty(Path).GetValue(entity, null).ToString();

                foreach (var item in list)
                {
                    string ipath = item.GetType().GetProperty(Path).GetValue(item, null).ToString();

                    if (string.IsNullOrEmpty(mparent))
                    {
                        item.GetType().GetProperty(Path).SetValue(item, epath + "." + ipath, null);
                    }
                    else
                    {
                        item.GetType().GetProperty(Path).SetValue(item, ipath.Replace(mpath, epath).Trim('.'), null);
                    }
                }

                #endregion

                return base.Update(entity) + Update(list);
            }
            else { return base.Update(entity); }
        }

        /// <summary>
        /// 重写删除方法
        /// </summary>
        /// <param name="id">主键编号</param>
        /// <returns>受影响行数</returns>
        public override int Delete(object id)
        {
            return base.Delete(id) + Delete(FuncChildren(id.ToString()));
        }

        /// <summary>
        /// 更新Path
        /// </summary>
        /// <param name="entity">要更新的实体</param>
        /// <returns>更新后的实体</returns>
        public virtual T UpdatePath(T entity)
        {
            string parent = entity.GetType().GetProperty(Parent).GetValue(entity, null).ToString();

            if (!string.IsNullOrEmpty(parent))
            {
                T model = GetByKey(parent);

                string path = model.GetType().GetProperty(Path).GetValue(model, null).ToString();

                if (string.IsNullOrEmpty(path))
                {
                    entity.GetType().GetProperty(Path).SetValue(entity, parent, null);
                }
                else { entity.GetType().GetProperty(Path).SetValue(entity, path + "." + parent, null); }
            }
            else { entity.GetType().GetProperty(Path).SetValue(entity, string.Empty, null); }

            return entity;
        }

        /// <summary>
        /// 根据主键编号获取子集
        /// </summary>
        /// <param name="id">主键编号</param>
        /// <returns>符合条件的结果集合</returns>
        public virtual Expression<Func<T, bool>> FuncChildren(string id)
        {
            ParameterExpression left = Expression.Parameter(typeof(T), "p");
            Expression expression = Expression.Constant(false);
            MemberExpression childmember = Expression.Property(left, typeof(T).GetProperty(Path));
            MethodInfo childmethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
            Expression childright = Expression.Call(childmember, childmethod, Expression.Constant(id));

            expression = Expression.Or(childright, expression);

            return Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { left });
        }
    }
}
